package org.apache.lucene.ocean.util;

import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;

/**
 * From Apache HttpClient.  Limit the reading of the RandomAccessFile to the number of bytes specified.  
 *
 */
public class LimitedFileInputStream extends InputStream {
  private RandomAccessFile randomAccessFile;
  /**
   * The maximum number of bytes that can be read from the stream. Subsequent
   * read operations will return -1.
   */
  private long contentLength;

  /** The current position */
  private long pos = 0;

  /** True if the stream is closed. */
  private boolean closed = false;

  public LimitedFileInputStream(RandomAccessFile randomAccessFile, int contentLength) {
    this.randomAccessFile = randomAccessFile;
    this.contentLength = contentLength;
  }

  public void close() throws IOException {
  }

  /**
   * Read the next byte from the stream
   * 
   * @return The next byte or -1 if the end of stream has been reached.
   * @throws IOException
   *           If an IO problem occurs
   * @see java.io.InputStream#read()
   */
  public int read() throws IOException {
    if (closed) {
      throw new IOException("Attempted read from closed stream.");
    }

    if (pos >= contentLength) {
      return -1;
    }
    pos++;
    return randomAccessFile.read();
  }

  /**
   * Does standard {@link InputStream#read(byte[], int, int)} behavior, but also
   * notifies the watcher when the contents have been consumed.
   * 
   * @param b
   *          The byte array to fill.
   * @param off
   *          Start filling at this position.
   * @param len
   *          The number of bytes to attempt to read.
   * @return The number of bytes read, or -1 if the end of content has been
   *         reached.
   * 
   * @throws java.io.IOException
   *           Should an error occur on the wrapped stream.
   */
  public int read(byte[] b, int off, int len) throws IOException {
    if (closed) {
      throw new IOException("Attempted read from closed stream.");
    }

    if (pos >= contentLength) {
      return -1;
    }

    if (pos + len > contentLength) {
      len = (int) (contentLength - pos);
    }
    int count = randomAccessFile.read(b, off, len);
    pos += count;
    return count;
  }

  /**
   * Read more bytes from the stream.
   * 
   * @param b
   *          The byte array to put the new data in.
   * @return The number of bytes read into the buffer.
   * @throws IOException
   *           If an IO problem occurs
   * @see java.io.InputStream#read(byte[])
   */
  public int read(byte[] b) throws IOException {
    return read(b, 0, b.length);
  }

  /**
   * Skips and discards a number of bytes from the input stream.
   * 
   * @param n
   *          The number of bytes to skip.
   * @return The actual number of bytes skipped. <= 0 if no bytes are skipped.
   * @throws IOException
   *           If an error occurs while skipping bytes.
   * @see InputStream#skip(long)
   */
  public long skip(long n) throws IOException {
    // make sure we don't skip more bytes than are
    // still available
    long length = Math.min(n, contentLength - pos);
    // skip and keep track of the bytes actually skipped
    long fp = length + randomAccessFile.getFilePointer();
    randomAccessFile.seek(fp);
    // only add the skipped bytes to the current position
    // if bytes were actually skipped
    if (length > 0) {
      pos += length;
    }
    return length;
  }

  public int available() throws IOException {
    if (this.closed) {
      return 0;
    }
    int avail = (int) (randomAccessFile.length() - randomAccessFile.getFilePointer());
    if (this.pos + avail > this.contentLength) {
      avail = (int) (this.contentLength - this.pos);
    }
    return avail;
  }
}
